#ifndef TREE_H
#define TREE_H


#include <XmlTree/XmlTree.h>
#include "../interface.inl"
#include "../nativeTypes.h"

#include <new>

#define ITREE(var)      castTo<ITree>(var)
#define INODE(var)      castTo<INode>(var)


namespace awa {

  namespace xcom {
    
    class INode : public AwaInterface<INode>, public awa::XercesDOMNode {
    
      typedef Array<INode>     iterator;

      INode() {}
    public :
    INode(awa::XercessDOMNode* base) : awa::XercesDOMNode(*base) {}

    INode(const INode& x) : awa::XercesDOMNode((awa::XercesDOMNode*)&x) {}

      Generic nodename() {
	return Generic(Interface(nodename));
      }

      Generic nodevalue() {
	return Generic(Interface(nodevalue));
      }

      Generic namespaceuri() {
	return Generic(Interface(namespaceuri));
      }

      Generic prefx() {
	return Generic(Interface(prefx));
      }

      Generic localname() {
	return Generic(Interface(localname));
      }

      Generic baseuri() {
	return Generic(Interface(baseuri));
      }

      Generic type() {
	return Generic(Interface(type));
      }

    };
    
    class ITree : public AwaInterface<ITree>, public awa::AbstractTree {
      
      typedef Array<ITree>     iterator;

      ITree() {}
    public :
      
    ITree(awa::AbstractTree* base): awa::AbstractTree(*base) {}
    ITree(const ITree& x) : awa::AbstractTree((awa::AbstractTree*)&x) {}
      Generic iseq(const Generic& rhs) {
	
	return Generic(((awa::AbstractTree)*this) == ((awa::AbstractTree)ITREE(rhs)));

      }
      Generic lt(const Generic& rhs) {

	return Generic(((awa::AbstractTree)*this) < ((awa::AbstractTree)ITREE(rhs)));
	
      }
      Generic isneq(const Generic& rhs) {

	return Generic(((awa::AbstractTree)*this) != ((awa::AbstractTree)ITREE(rhs)));
      }
      Generic gt(const Generic& rhs) {

	return Generic(((awa::AbstractTree)*this) > ((awa::AbstractTree)ITREE(rhs)));
      }
      Generic le(const Generic& rhs) {

	return Generic(((awa::AbstractTree)*this) <= ((awa::AbstractTree)ITREE(rhs)));
      }
      Generic ge(const Generic& rhs) {

	return Generic(((awa::AbstractTree)*this) >= ((awa::AbstractTree)ITREE(rhs)));
      }

      Generic begin() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->begin()));
	return Generic(it);
      }
      Generic end() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->end()));
	return Generic(it);
      }
      
      Generic node_begin() {
	ITRee::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->node_begin()));
	return Generic(it);
      }
      Generic node_end() {
	ITRee::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->node_end()));
	return Generic(it);
      }
      
      Generic rbegin() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->rbegin()));
	return Generic(it);
      }
      Generic rend() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->rend()));
	return Generic(it);
      }
      
      Generic pre_order_begin() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->pre_order_begin()));
	return Generic(it);
      }
      Generic pre_order_end() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->pre_order_end()));
	return Generic(it);
      }
      
      Generic post_order_begin() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->post_order_begin()));
	return Generic(it);
      }
      Generic post_order_end() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->post_order_end()));
	return Generic(it);
      }
      Generic level_order_begin() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->level_order_begin()));
	return Generic(it);
      }
      Generic level_order_end() {
	INode::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) INode(*(this->level_order_end()));
	return Generic(it);
      }
      
      Generic node_rbegin() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->node_rbegin()));
	return Generic(it);
      }
      Generic node_rend() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->node_rend()));
	return Generic(it);
      }
      
      Generic pre_order_node_begin() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->pre_order_node_begin()));
	return Generic(it);

      }
      Generic pre_order_node_end() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->pre_order_node_end()));
	return Generic(it);
      }
      Generic post_order_node_begin() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->post_order_node_begin()));
	return Generic(it);
      }
      Generic post_order_node_end() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->post_order_node_end()));
	return Generic(it);
      }
      Generic level_order_node_begin() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->level_order_node_begin()));
	return Generic(it);
      }
      Generic level_order_node_end() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree(*(this->level_order_node_end()));
	return Generic(it);
      }
      
      Generic reserve(const Generic& sz) { // return Array<ITree>, prepend _
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	// copy constructor is a must for all objects
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->reserve(castTo<unsigned long>(sz));
	return Generic(it);
      }
      
      Generic front() {
	return Generic(ITree(&((awa::AbstractTree*)this)->front()));
      }
      Generic back() {
	return Generic(ITree(&((awa::AbstractTree*)this)->back()));
      }
      
      Generic push_back(const Generic& value) { // return Array<ITree> _
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->push_back(INode(value));
	return Generic(it);
      }
      Generic pop_back() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->pop_back();
	return Generic(it);
      }
      Generic insert_element(const Generic& value) {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->insert_element(INODE(value));
	return Generic(it);
      }
      Generic insert_tree(const Generic& tree_obj) {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->insert_tree(ITREE(tree_obj));
	return Generic(it);
      }
      
      Generic set_element(const Generic& value) {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->set_element(INODE(value));
	return Generic(it);
      }
      Generic set_tree(const Generic& tree_obj) {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->set_tree(ITREE(tree_obj));
	return Generic(it);
      }
      
      Generic clear() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->clear();
	return Generic(it);
      }
      
      Generic child_index(const Generic& index) {
	return Generic(ITree(&(*this)[castTo<unsigned long>(index)]));
      }
      
      Generic sort() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->sort();
	return Generic(it);
      }
      
      Generic sort_descendants() {
	ITree::iterator it;
	it.allocate(Generic(unsigned long(1)));
	new(it.ref()) ITree((awa::AbstractTree*)this);
	((awa::AbstractTree*)it->ref())->sort_descendants();
	return Generic(it);
      }
    };
    
  };
  
};

#endif
